FSharp.Data.SqlClient


Configuration and Input

SqlCommandProvider parameters

NameDefaultAccepted values
CommandText-T-SQL script or *.sql file
ConnectionStringOrName-Connection string or name
ResultTypeResultType.RecordsTuples, Records, DataTable, or DataReader
SingleRowfalsetrue/false
ConfigFileapp.config or web.configValid file name
AllParametersOptionalfalsetrue/false
ResolutionFolderThe folder that contains the project or script.Valid file system path. Absolute or relative.
DataDirectoryThe name of the data directory that replaces |DataDirectory| in connection strings. The default value is the project or script directory.Valid file system path.

SqlProgrammabilityProvider parameters

NameDefaultAccepted values
ConnectionStringOrName-Connection string or name
ConfigFileapp.config or web.configvalid file name
ResolutionFolderThe folder that contains the project or script.Valid file system path. Absolute or relative.
DataDirectoryThe name of the data directory that replaces |DataDirectory| in connection strings. The default value is the project or script directory.Valid file system path.
UseReturnValuefalseSupport for stored procedure return value.

CommandText

T-SQL script

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
open FSharp.Data

[<Literal>]
let connStr = @"Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=True"

//Inline T-SQL text convinient for short queries 
type GetDate = SqlCommandProvider<"SELECT GETDATE() AS Now", connStr>

//More complex queries are better off extracted to stand-alone literals

//Fibonacci! Not again! :)
[<Literal>]
let fibonacci = "
    WITH Fibonacci ([N-1], N) AS
    ( 
        --seed
	    SELECT CAST(0 AS BIGINT), CAST(1 AS BIGINT)

	    UNION ALL
        --fold
	    SELECT N, [N-1] + N
	    FROM Fibonacci
    )

    SELECT TOP(@Top) [N-1] 
    FROM Fibonacci
"

type FibonacciQuery = SqlCommandProvider<fibonacci, connStr>

do 
    let cmd = new FibonacciQuery(connStr)

    cmd.Execute(10L) 
    |> Seq.map Option.get 
    |> Seq.toArray 
    |> printfn "First 10 fibonacci numbers: %A" 

External *.sql file

An ability to use external *.sql file instead of inline strings can improve developement experience. Visual Studio has rich tooling support for *.sql files. (via SQL Server Data Tools)

It offers following benefits:

  • Intellisense in both F# and T-SQL code (it cannot get better)
  • T-SQL syntax highlighting and verification
  • Testing: query execution gives immediate feedback (small trick required - see the picture above)
  • Clean separation between T-SQL and F# code

Having all data access layer logic in bunch of files in one location has clear advantage. For example, it can be handed over to DBA team for optimization. It's harder to do when application and data access are mixed together (LINQ).

1: 
2: 
let cmd = new SqlCommandProvider<const(SqlFile<"GetDate.sql">.Text), connStr>(connStr)
cmd.Execute() |> ignore

Extracting T-SQL into external files is not the only way to scale application development. The other alternative is to push logic into programmable objects. I strongly recommend T-SQL functions because they have typical benefits of functional-first programming style: composition (therefore reuse), restricted side-effects and simple substitution model (easy to reason about). Stored procedures can be used too but they resemble imperative programming with all the drawbacks attached.

Below is an example of SQL Table-Valued Function usage.

1: 
2: 
type GetContactInformation = 
    SqlCommandProvider<"SELECT * FROM dbo.ufnGetContactInformation(@PersonId)", connStr>

Syntax errors

The type provider shows fairly clear error message if there are any syntax errors in T-SQL. An instantaneous feedback is one of the most handy features of SqlCommandProvider.

Limitation: a single parameter in a query may only be used once.

For example, an attempt to use following query will fail:

WHEN @x % 3 = 0 AND @x % 5 = 0 THEN &#39;FizzBuzz&#39; 
WHEN @x % 3 = 0 THEN &#39;Fizz&#39; 
WHEN @x % 5 = 0 THEN &#39;Buzz&#39; 
ELSE CAST(@x AS NVARCHAR) 

You can work around this by declaring a local intermediate variable in t-sql script and assigning a parameter in question to that variable.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
type FizzOrBuzz = SqlCommandProvider<"
    DECLARE @x AS INT = @xVal
    SELECT 
	    CASE 
		    WHEN @x % 3 = 0 AND @x % 5 = 0 THEN 'FizzBuzz' 
		    WHEN @x % 3 = 0 THEN 'Fizz' 
		    WHEN @x % 5 = 0 THEN 'Buzz' 
		    ELSE CONCAT(@x, '') --use concat to avoid nullable column
	    END", connStr>

let fizzOrBuzz = new FizzOrBuzz(connStr)
printfn "Answer on interview:\n%A" [ for i = 1 to 100 do yield! fizzOrBuzz.Execute(i) ]

ConnectionStringOrName

Inline or literal

Connection string can be provided either via literal (all examples above) or inline

1: 
2: 
3: 
//Inline 
type Get42 = 
    SqlCommandProvider<"SELECT 42", @"Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=True">

The literal version is more practical because connection string definition can be shared between different declarations of SqlCommandProvider<...>.

By name

The other option is to supply connection string name from config file.

1: 
2: 
3: 
4: 
5: 
//default config file name is app.config or web.config
type Get43 = SqlCommandProvider<"SELECT 43", "name=AdventureWorks">

//specify ANY other file name (including web.config) explicitly
type Get44 = SqlCommandProvider<"SELECT 44", "name=AdventureWorks", ConfigFile = "user.config">

I would like to emphasize that ConfigFile is about design time only. Let me give you couple examples to clarify:

  • You build Windows Service or WPF application.



    If it is a purely F# project and default app.config is there ConfigFile parameter can be omitted. Still, in runtime the connection string with the same name should be available via .NET configuration infrastructure (ConfigurationManager). It means that either you have packaging/deployment system that knows how to fix connection string in config file to point to production database (for example, Slow Cheetah), or you do it manually after application is deployed.

  • You have mixed ASP.NET WebAPI solution: C# hosting project and F# controllers implementation project.



    F# controllers project is a simple library project. It has data access layer module. SqlCommandProvider<...> definitions refer to connection string by name form user.config file.



    It's completely legitimate not to check-in this user.config file into source control system if it's developer specific. Similar setup can be applied inside single project to separate user-specific configuration from common production config.

Overriding connection string at run-time

Run-time database connectivity configuration is rarely (almost never) the same as design-time. All SqlCommandProvider<_>-generated types can be re-configured at run-time via optional constructor parameter. The parameter is optional because "config file + name" approach is an acceptable way to have run-time configuration different from design-time. Several use cases are possible:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
//Case 1: pass run-time connection string into ctor
let runTimeConnStr = "..." //somehow get connection string at run-time
let get42 = new Get42(runTimeConnStr)

//Case 2: bunch of command types, single database
//Factory or IOC of choice to avoid logic duplication. Use F# ctor static constraints.
module DB = 
    [<Literal>]
    let connStr = @"Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=True"

    open System.Data.SqlClient

    type MyCmd1 = SqlCommandProvider<"SELECT 42", connStr>
    type MyCmd2 = SqlCommandProvider<"SELECT 42", connStr>

    let inline createCommand() : 'a = 
        let runtimeConnStr = "..." //somehow get connection string at run-time
        //invoke ctor
        (^a : (new : string * int -> ^a) (runtimeConnStr, 30)) 
        //or
        //(^a : (static member Create: string -> ^a) runtimeConnStr) 

let dbCmd1: DB.MyCmd1 = DB.createCommand()
let dbCmd2: DB.MyCmd2 = DB.createCommand()

//Case 3: multiple databases
//It gets tricky because we need to distinguish between command types associated with different databases. 
//Static type property ConnectionStringOrName that has exactly same value as passed into SqlCommandProvider helps.
module DataAccess = 
    [<Literal>]
    let adventureWorks = @"Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=True"
    [<Literal>]
    let master = @"Data Source=.;Initial Catalog=master;Integrated Security=True"

    type MyCmd1 = SqlCommandProvider<"SELECT 42", adventureWorks>
    type MyCmd2 = SqlCommandProvider<"SELECT 42", master>

    let inline createCommand() : 'a = 
        let designTimeConnectionString = (^a : (static member get_ConnectionStringOrName : unit -> string) ())
        let connStr = 
            if designTimeConnectionString = adventureWorks  
            then "..." //somehow get AdventureWorks connection string at run-time
            elif designTimeConnectionString = master
            then "..." //somehow get master connection string at run-time
            else failwith "Unexpected"
        //invoke ctor
        (^a : (new : string * int -> ^a) (connStr, 30)) 

let adventureWorksCmd: DataAccess.MyCmd1 = DataAccess.createCommand()
let masterCmd: DataAccess.MyCmd2 = DataAccess.createCommand()

Another related case, albeit not that common, is local transaction.

Important: SqlConnection associated with passed transaction, is not closed automatically in this case. It is responsibility of the client code to close and dispose it.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
[<Literal>]
let bitCoinCode = "BTC"
[<Literal>]
let bitCoinName = "Bitcoin"

type DeleteBitCoin = 
    SqlCommandProvider<"DELETE FROM Sales.Currency WHERE CurrencyCode = @Code"
                        , connStr>
type InsertBitCoin = 
    SqlCommandProvider<"INSERT INTO Sales.Currency VALUES(@Code, @Name, GETDATE())"
                        , connStr>
type GetBitCoin = 
    SqlCommandProvider<"SELECT CurrencyCode, Name FROM Sales.Currency WHERE CurrencyCode = @code"
                        , connStr>

do 
    let cmd = new DeleteBitCoin(connStr) in cmd.Execute(bitCoinCode) |> ignore
    let conn = new System.Data.SqlClient.SqlConnection(connStr)
    conn.Open()
    let tran = conn.BeginTransaction()

    let cmd = 
        new SqlCommandProvider<"
            INSERT INTO Sales.Currency VALUES(@Code, @Name, GETDATE())
        ", connStr>(connStr)

    use insert = InsertBitCoin.Create(conn, transaction = tran) 
    assert(insert.Execute(bitCoinCode, bitCoinName) = 1)

    use get = new GetBitCoin(conn, transaction = tran)
    assert( get.Execute(bitCoinCode) |> Seq.length = 1)

    tran.Rollback()

    assert( GetBitCoin.Create(connStr).Execute(bitCoinCode) |> Seq.length = 0)

It is worth noting that because of "erased types" nature of this type provider reflection and other dynamic techniques cannot be used to create command instances.

SqlProgrammabilityProvider<...> supports connection name syntax as well.

1: 
2: 
3: 
open System

type AdventureWorks2012 = SqlProgrammabilityProvider<connStr>

Optional input parameters

By default all input parameters of AsyncExecute/Execute generated by SqlCommandProvider<...> are mandatory. But there are rare cases when you prefer to handle NULL input values inside T-SQL script. AllParametersOptional set to true makes all parameters (guess what) optional.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
type IncrBy = SqlCommandProvider<"SELECT @x + ISNULL(CAST(@y AS INT), 1) ", 
                                    connStr, 
                                    AllParametersOptional = true, 
                                    SingleRow = true>
let incrBy = new IncrBy(connStr)
//pass both params passed 
incrBy.Execute(Some 10, Some 2) = Some( Some 12) //true
//omit second parameter. default to 1
incrBy.Execute(Some 10) = Some( Some 11) //true

Note that AllParametersOptional is not supported by SQlProgrammabilityProvider<...> as it is able to infer default values for Stored Procedures and UDFs so AsyncExecute signature makes corresponding parameters optional.

Table-valued parameters (TVPs)

Sql command needs to call a stored procedure or user-defined function that takes a parameter of table-valued type.

Set up sample type and sproc:


CREATE TYPE dbo.myTableType AS TABLE (myId int not null, myName nvarchar(30) null) 
GO 
CREATE PROCEDURE myProc 
   @p1 dbo.myTableType readonly 
AS 
BEGIN 
   SELECT myName from @p1 p 
END 

1: 
2: 
3: 
4: 
5: 
type TableValuedSample = SqlCommandProvider<"exec myProc @x", connStr>
type TVP = TableValuedSample.MyTableType
let tvpSp = new TableValuedSample(connStr)
//nullable columns mapped to optional ctor params
tvpSp.Execute(x = [ TVP(myId = 1, myName = Some "monkey"); TVP(myId = 2) ]) 

Same with SqlProgrammabilityProvider<...>

1: 
2: 
3: 
4: 
5: 
type T = AdventureWorks2012.dbo.``User-Defined Table Types``.MyTableType

do 
    use cmd = new AdventureWorks2012.dbo.MyProc(connStr)
    cmd.Execute([ T(myId = 2); T(myId = 1) ]) |> printfn "%A"

Stored procedures

Command types generated by SqlProgrammabilityProvider<...> largely have same interface with exceptions: There is no static Create factory method because intellisense issue doesn’t exist for these types There is additional ExecuteSingle/ AsyncExecuteSingle to opt-in for singleton result set.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
do 
    use cmd = new AdventureWorks2012.dbo.uspGetWhereUsedProductID(connStr)

    //sync
    cmd.Execute( StartProductID = 1, CheckDate = DateTime(2013,1,1)) |> printfn "%A"

    //async
    cmd.AsyncExecute( StartProductID = 1, CheckDate = DateTime(2013,1,1)) 
    |> Async.RunSynchronously 
    |> Array.ofSeq
    |> printfn "%A"

Stored Procedures output parameters are mapped into F# byref method parameters. Because byref parameters cannot be combined with lazily evaluated computation expression, AsyncExecute and AsyncExecuteSingle methods are not provided.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
do 
    use cmd = new AdventureWorks2012.dbo.uspLogError(connStr)
    let errorLogId = ref -1
    let recordsAffected = cmd.Execute(errorLogId)
    printfn "errorLogId: %i" !errorLogId

do  //tupled invocation syntax
    //works only in VS 2015 or later because of F# compiler bug
    use cmd = new AdventureWorks2012.dbo.uspLogError(connStr)
    let _, errorLogId = cmd.Execute()
    printfn "errorLogId: %i" errorLogId

do  //mutable bindgings 
    use cmd = new AdventureWorks2012.dbo.uspLogError(connStr)
    let mutable errorLogId = -1
    let recordsAffected = cmd.Execute(&errorLogId)
    printfn "errorLogId: %i" errorLogId

By default stored procedure return values are not surfaced. To make it available specify UseReturnValue = true static parameter of SqlProgrammabilityProvider. RETURN_VALUE will be the last byref parameter.

1: 
2: 
3: 
4: 
5: 
do 
    use cmd = 
        new SqlProgrammabilityProvider<connStr, UseReturnValue = true>.dbo.uspLogError(connStr)
    let recordsAffected, errorLogId, returnValue = cmd.Execute()
    printfn "recordsAffected: %i, errorLogId: %i, returnValue: %i" recordsAffected errorLogId returnValue

Things get interesting when stored procedure return both set of rows and output parameters. I won't show any sample code because AdventureWorks database doesn't have such procedure. But the only change comparing to non-query stored procedure that instead of returning number of affected records it returns F# list of records. Notice that list is data structure as oppose to lazy evaluated seq<_> computation. This caused by a fact Sql Server + ADO.NET populates output parameter only after row set reader is closed. See [http://stackoverflow.com/questions/65662/output-parameters-not-readable-when-used-with-a-datareader].

namespace FSharp
namespace FSharp.Data
Multiple items
type LiteralAttribute =
  inherit Attribute
  new : unit -> LiteralAttribute

Full name: Microsoft.FSharp.Core.LiteralAttribute

--------------------
new : unit -> LiteralAttribute
val connStr : string

Full name: Configuration and Input.connStr
type GetDate = obj

Full name: Configuration and Input.GetDate
val fibonacci : string

Full name: Configuration and Input.fibonacci
type FibonacciQuery = obj

Full name: Configuration and Input.FibonacciQuery
val cmd : FibonacciQuery
module Seq

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
module Option

from Microsoft.FSharp.Core
val get : option:'T option -> 'T

Full name: Microsoft.FSharp.Core.Option.get
val toArray : source:seq<'T> -> 'T []

Full name: Microsoft.FSharp.Collections.Seq.toArray
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val cmd : obj

Full name: Configuration and Input.cmd
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
type GetContactInformation = obj

Full name: Configuration and Input.GetContactInformation
type FizzOrBuzz = obj

Full name: Configuration and Input.FizzOrBuzz
val fizzOrBuzz : FizzOrBuzz

Full name: Configuration and Input.fizzOrBuzz
val i : int
type Get42 = obj

Full name: Configuration and Input.Get42
type Get43 = obj

Full name: Configuration and Input.Get43
type Get44 = obj

Full name: Configuration and Input.Get44
val runTimeConnStr : string

Full name: Configuration and Input.runTimeConnStr
val get42 : Get42

Full name: Configuration and Input.get42
val connStr : string

Full name: Configuration and Input.DB.connStr
namespace System
namespace System.Data
namespace System.Data.SqlClient
type MyCmd1 = obj

Full name: Configuration and Input.DB.MyCmd1
type MyCmd2 = obj

Full name: Configuration and Input.DB.MyCmd2
val createCommand : unit -> 'a (requires member ( .ctor ))

Full name: Configuration and Input.DB.createCommand
val runtimeConnStr : string
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val dbCmd1 : DB.MyCmd1

Full name: Configuration and Input.dbCmd1
module DB

from Configuration and Input
val dbCmd2 : DB.MyCmd2

Full name: Configuration and Input.dbCmd2
val adventureWorks : string

Full name: Configuration and Input.DataAccess.adventureWorks
val master : string

Full name: Configuration and Input.DataAccess.master
type MyCmd1 = obj

Full name: Configuration and Input.DataAccess.MyCmd1
type MyCmd2 = obj

Full name: Configuration and Input.DataAccess.MyCmd2
val createCommand : unit -> 'a (requires member get_ConnectionStringOrName and member ( .ctor ))

Full name: Configuration and Input.DataAccess.createCommand
val designTimeConnectionString : string
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val connStr : string
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val adventureWorksCmd : DataAccess.MyCmd1

Full name: Configuration and Input.adventureWorksCmd
module DataAccess

from Configuration and Input
val masterCmd : DataAccess.MyCmd2

Full name: Configuration and Input.masterCmd
val bitCoinCode : string

Full name: Configuration and Input.bitCoinCode
val bitCoinName : string

Full name: Configuration and Input.bitCoinName
type DeleteBitCoin = obj

Full name: Configuration and Input.DeleteBitCoin
type InsertBitCoin = obj

Full name: Configuration and Input.InsertBitCoin
type GetBitCoin = obj

Full name: Configuration and Input.GetBitCoin
val cmd : DeleteBitCoin
val conn : System.Data.SqlClient.SqlConnection
Multiple items
type SqlConnection =
  inherit DbConnection
  new : unit -> SqlConnection + 1 overload
  member BeginTransaction : unit -> SqlTransaction + 3 overloads
  member ChangeDatabase : database:string -> unit
  member Close : unit -> unit
  member ConnectionString : string with get, set
  member ConnectionTimeout : int
  member CreateCommand : unit -> SqlCommand
  member DataSource : string
  member Database : string
  member EnlistDistributedTransaction : transaction:ITransaction -> unit
  ...

Full name: System.Data.SqlClient.SqlConnection

--------------------
System.Data.SqlClient.SqlConnection() : unit
System.Data.SqlClient.SqlConnection(connectionString: string) : unit
val tran : System.Data.SqlClient.SqlTransaction
val cmd : obj
val insert : System.IDisposable
val get : GetBitCoin
val length : source:seq<'T> -> int

Full name: Microsoft.FSharp.Collections.Seq.length
System.Data.SqlClient.SqlTransaction.Rollback() : unit
System.Data.SqlClient.SqlTransaction.Rollback(transactionName: string) : unit
type AdventureWorks2012 = obj

Full name: Configuration and Input.AdventureWorks2012
type IncrBy = obj

Full name: Configuration and Input.IncrBy
val incrBy : IncrBy

Full name: Configuration and Input.incrBy
union case Option.Some: Value: 'T -> Option<'T>
type TableValuedSample = obj

Full name: Configuration and Input.TableValuedSample
type TVP = obj

Full name: Configuration and Input.TVP
val tvpSp : TableValuedSample

Full name: Configuration and Input.tvpSp
type T = obj

Full name: Configuration and Input.T
val cmd : IDisposable
Multiple items
type DateTime =
  struct
    new : ticks:int64 -> DateTime + 10 overloads
    member Add : value:TimeSpan -> DateTime
    member AddDays : value:float -> DateTime
    member AddHours : value:float -> DateTime
    member AddMilliseconds : value:float -> DateTime
    member AddMinutes : value:float -> DateTime
    member AddMonths : months:int -> DateTime
    member AddSeconds : value:float -> DateTime
    member AddTicks : value:int64 -> DateTime
    member AddYears : value:int -> DateTime
    ...
  end

Full name: System.DateTime

--------------------
DateTime()
   (+0 other overloads)
DateTime(ticks: int64) : unit
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : unit
   (+0 other overloads)
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:Threading.CancellationToken -> 'T
type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : params indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...

Full name: System.Array
val ofSeq : source:seq<'T> -> 'T []

Full name: Microsoft.FSharp.Collections.Array.ofSeq
val errorLogId : int ref
Multiple items
val ref : value:'T -> 'T ref

Full name: Microsoft.FSharp.Core.Operators.ref

--------------------
type 'T ref = Ref<'T>

Full name: Microsoft.FSharp.Core.ref<_>
val recordsAffected : obj
val errorLogId : int
val mutable errorLogId : int
val recordsAffected : int
val returnValue : int
Fork me on GitHub